#! /usr/bin/python

from ctypes import *
from sys import stdin

twofish_mod_name = "libtwofish.so"
lib_loader = cdll
twofish_mod = lib_loader.LoadLibrary(twofish_mod_name)

def rol(word, bits):
	c_word = c_uint(word)
	c_bits = c_int(bits)
	c_result = twofish_mod.rol(c_word, c_bits)
	return c_result

def ror(word, bits):
	c_word = c_uint(word)
	c_bits = c_int(bits)
	c_result = twofish_mod.ror(c_word, c_bits)
	return c_result

def s0(value, key1, key2):
	result = twofish_mod.q0_perm(value)
	result = result ^ key1
	result = twofish_mod.q0_perm(value)
	result = result ^ key2
	result = twofish_mod.q1_perm(value)
	return result

def main():
	print "Start initial"
	candicates = [i for i in range(0, 0xffff+1)]
	print "Initial complete!"
	try_times = 1
	while True:
		plaintext = stdin.readline().strip()
		ciphertext = stdin.readline().strip()
		plaintext_e = stdin.readline().strip()
		ciphertext_e = stdin.readline().strip()

		print plaintext
		print ciphertext
		print plaintext_e
		print ciphertext_e

		if not plaintext or not ciphertext or not plaintext_e or not ciphertext_e:
			print "Not enough sample!"
			break

		print 'Try time:%d' % try_times
		try_times += 1
		
		tmp_list = plaintext.split(" ")
		print tmp_list
		plaintext_list = [int(each_byte, 16) for each_byte in tmp_list]

		tmp_list = ciphertext.split(" ")
		print tmp_list
		ciphertext_list = [int(each_byte, 16) for each_byte in tmp_list]

		tmp_list = plaintext.split(" ")
		plaintext_e_list = [int(each_byte, 16) for each_byte in tmp_list]

		tmp_list = plaintext.split(" ")
		ciphertext_e_list = [int(each_byte, 16) for each_byte in tmp_list]

		l30 = ciphertext_list[3]
		for i in range(2,-1, -1):
			l30 = (l30 << 8) | ciphertext_list[i]

		l30_e = ciphertext_e_list[3];
		for i in range(2,-1,-1):
			l30_e = (l30_e << 8) | ciphertext_e_list[i]

		r00 = plaintext_list[11]
		for i in range(10, 7, -1):
			r00 = (r00 << 8) | plaintext_list[i]

		r00_e = plaintext_e_list[11]
		for i in range(10, 7, -1):
			r00_e = (r00_e << 8) | plaintext_e_list[i]


		l30 = rol(l30, 1)
		l30_e = rol(l30_e, 1)

		r00 = ror(r00, 1)
		r00_e = ror(r00_e, 1)

		out_diff = l30 ^ l30_e ^ r00 ^ r00_e

		out_diff_0 = out_diff & 0xff

		sbox_in = ciphertext_list[8] 
		sbox_in_e = ciphertext_e_list[8]
	
		in_diff_set = set()
		for try_in_diff in range(0, 0x100):
			found = False
			for try_in in range(0, 0x100):
				for try_adder in range(0, 0x100):
					try_in_e = try_in ^ try_in_diff
					out = (try_in + try_adder) & 0xff
					out_e = (try_in_e + try_adder) & 0xff
					out_diff = out ^ out_e
					if out_diff == out_diff_0:
						found = True
						break
				if found:
					break
			if found:
				in_diff_set.add(try_in_diff)
		
		tmp_set = []
		for cand in candicates:
			key1 = cand & 0xff
			key2 = (cand >> 8) & 0xff
			
			rt = s0(sbox_in, key1, key2)
			rt_e = s0(sbox_in_e, key1, key2)

			in_diff = rt ^ rt_e
			if in_diff in in_diff_set:
				tmp_set.append(cand)

		candicates = tmp_set
		print "candicates size:%d" % len(candicates)
		if len(candicates) == 1:
			value = candicates[0]
			print "key1:%.2x, key2:%.2x" % (value & 0xff, (value>>8) & 0xff)
			exit(0)


if __name__ == '__main__':
	main()
